package com.ddl.web.elastic.service;

import com.ddl.model.PageDomain;
import com.ddl.model.TableDataInfo;
import com.ddl.web.elastic.domain.Item;
import com.ddl.web.elastic.mapper.ItemRepository;

import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.StringTerms;
import org.elasticsearch.search.aggregations.metrics.avg.InternalAvg;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class ItemService {

	@Autowired
	private ElasticsearchTemplate elasticsearchTemplate;

	@Autowired
	private ItemRepository itemRepository;
	/**
	 * 创建索引
	 */
	public boolean createIndex() {
		// 创建索引，会根据Item类的@Document注解信息来创建
		boolean index = elasticsearchTemplate.createIndex(Item.class);
		// 配置映射，会根据Item类中的id、Field等字段来自动完成映射
		boolean type = elasticsearchTemplate.putMapping(Item.class);
		return type;
	}

	/**
	 * 删除索引
	 */
	public void deleteIndex() {
		elasticsearchTemplate.deleteIndex("item");
	}

	/**
	 * 新增
	 */
	public void insert() {
		Item item = new Item("1111", "小米手机7", "手机", "小米", 2999.00, "国产小米手机");
		itemRepository.save(item);
	}

	/**
	 * 批量新增
	 */
	public Iterable<Item> insertList() {
		List<Item> list = new ArrayList<>();
		list.add(new Item("2L", "坚果手机R1", "手机", "锤子", 3999.00, "国产坚果手机"));
		list.add(new Item("3L", "华为META20", "手机", "华为", 4999.00, "国产华为手机"));
		list.add(new Item("4L", "iPhone X", "手机", "iPhone", 5100.00, "美国的苹果手机"));
		list.add(new Item("5L", "iPhone XS", "手机", "iPhone", 5999.00, "美国的iphone"));
		// 接收对象集合，实现批量新增
		Iterable<Item> items = itemRepository.saveAll(list);
		return items;
	}

	/**
	 * 修改
	 *
	 * ：修改和新增是同一个接口，区分的依据就是id，这一点跟我们在页面发起PUT请求是类似的。
	 */

	/**
	 * 删除所有
	 */
	public void delete() {
		itemRepository.deleteAll();
	}

	/**
	 * 基本查询
	 */
	public void query() {
		// 查询全部，并按照价格降序排序
		Iterable<Item> items = itemRepository.findAll(Sort.by("price").descending());
		items.forEach(item -> System.out.println("item = " + item));
	}

	/**
	 * 自定义方法
	 */
	public void queryByPriceBetween() {
		// 根据价格区间查询
		List<Item> list = itemRepository.findByPriceBetween(5000.00, 6000.00);
		list.forEach(item -> System.out.println("item = " + item));
	}

	/**
	 * 自定义查询
	 */
	public TableDataInfo<Item> search(String title) {
		// 构建查询条件
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
		// 添加基本分词查询
		queryBuilder.withQuery(QueryBuilders.matchQuery("content", title));
		// 搜索，获取结果
		Page<Item> items = itemRepository.search(queryBuilder.build());
		// 总条数
		long total = items.getTotalElements();

		items.forEach(item -> System.out.println("item = " + item));
		return new TableDataInfo<Item>(items.toList(), (int) total);
	}

	/**
	 * 分页查询
	 */
	public void searchByPage() {
		// 构建查询条件
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
		// 添加基本分词查询
		queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));
		// 分页：
		int page = 0;
		int size = 2;
		queryBuilder.withPageable(PageRequest.of(page, size));
		// 搜索，获取结果
		Page<Item> items = itemRepository.search(queryBuilder.build());
		long total = items.getTotalElements();
		System.out.println("总条数 = " + total);
		System.out.println("总页数 = " + items.getTotalPages());
		System.out.println("当前页：" + items.getNumber());
		System.out.println("每页大小：" + items.getSize());
		items.forEach(item -> System.out.println("item = " + item));
	}

	/**
	 * 排序
	 */
	public void searchAndSort() {
		// 构建查询条件
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
		// 添加基本分词查询
		queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));
		// 排序
		queryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
		// 搜索，获取结果
		Page<Item> items = this.itemRepository.search(queryBuilder.build());
		// 总条数
		long total = items.getTotalElements();
		System.out.println("总条数 = " + total);
		items.forEach(item -> System.out.println("item = " + item));
	}

	/**
	 * 聚合为桶
	 */
	public void testAgg() {
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
		// 不查询任何结果
		queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
		// 1、添加一个新的聚合，聚合类型为terms，聚合名称为brands，聚合字段为brand
		queryBuilder.addAggregation(AggregationBuilders.terms("brands").field("brand"));
		// 2、查询,需要把结果强转为AggregatedPage类型
		AggregatedPage<Item> aggPage = (AggregatedPage<Item>) itemRepository.search(queryBuilder.build());
		// 3、解析
		// 3.1、从结果中取出名为brands的那个聚合，
		// 因为是利用String类型字段来进行的term聚合，所以结果要强转为StringTerm类型
		StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
		// 3.2、获取桶
		List<StringTerms.Bucket> buckets = agg.getBuckets();
		// 3.3、遍历
		for (StringTerms.Bucket bucket : buckets) {
			// 3.4、获取桶中的key，即品牌名称
			System.out.println(bucket.getKeyAsString());
			// 3.5、获取桶中的文档数量
			System.out.println(bucket.getDocCount());
		}
	}

	/**
	 * 嵌套聚合，求平均值
	 */
	public void testSubAgg() {
		NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
		// 不查询任何结果
		queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
		// 1、添加一个新的聚合，聚合类型为terms，聚合名称为brands，聚合字段为brand
		queryBuilder.addAggregation(
				AggregationBuilders.terms("brands").field("brand")
						.subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合，求平均值
		);
		// 2、查询,需要把结果强转为AggregatedPage类型
		AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
		// 3、解析
		// 3.1、从结果中取出名为brands的那个聚合，
		// 因为是利用String类型字段来进行的term聚合，所以结果要强转为StringTerm类型
		StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
		// 3.2、获取桶
		List<StringTerms.Bucket> buckets = agg.getBuckets();
		// 3.3、遍历
		for (StringTerms.Bucket bucket : buckets) {
			// 3.4、获取桶中的key，即品牌名称  3.5、获取桶中的文档数量
			System.out.println(bucket.getKeyAsString() + "，共" + bucket.getDocCount() + "台");

			// 3.6.获取子聚合结果：
			InternalAvg avg = (InternalAvg) bucket.getAggregations().asMap().get("priceAvg");
			System.out.println("平均售价：" + avg.getValue());
		}
	}
}
